/*
 * WRMDispatchingLayerProxy.java   0.0.1    Nov 20, 2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.net.ril.wrm;

import java.io.Serializable;

import org.mentalsmash.tazio.commons.TazioRuntimeException;
import org.mentalsmash.tazio.commons.identifiers.NID;
import org.mentalsmash.tazio.commons.identifiers.OID;
import org.mentalsmash.tazio.net.ril.DispatchingLayerProxy;
import org.mentalsmash.tazio.net.ril.RemoteInvocationMessageHandler;
import org.mentalsmash.tazio.net.ril.RemoteInvoker;
import org.mentalsmash.tazio.net.ril.exceptions.FailedMessageDeliveryException;
import org.mentalsmash.tazio.net.ril.exceptions.NoResponseReceivedException;
import org.mentalsmash.tazio.net.ril.impl.AbstractDispatchingLayerProxy;
import org.mentalsmash.tazio.net.ril.messages.MethodInvocationRequestMessage;
import org.mentalsmash.tazio.net.ril.messages.MethodInvocationResponseMessage;
import org.mentalsmash.tazio.net.ril.messages.OnGroupMethodInvocationRequestMessage;
import org.mentalsmash.tazio.net.ril.messages.RILConnectionRequest;
import org.mentalsmash.tazio.net.ril.messages.RILConnectionResponse;
import org.mentalsmash.tazio.net.ril.messages.RILMessage;
import org.mentalsmash.tazio.net.ril.messages.RILRequestMessage;
import org.mentalsmash.tazio.net.ril.messages.RILResponseMessage;
import org.mentalsmash.tazio.net.ril.messages.RegistryContentRequestMessage;
import org.mentalsmash.tazio.net.ril.messages.RegistryContentResponseMessage;
import org.mentalsmash.tazio.transparency.interfaces.InputMessageListener;
import org.mentalsmash.tazio.transparency.interfaces.Transparency;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.google.inject.internal.Preconditions;
import com.google.inject.name.Named;

/**
 * @version 0.0.1 Nov 20, 2009
 * @author Andrea Sorbini <as@mentalsmash.org>
 * 
 */
public class WRMDispatchingLayerProxy extends AbstractDispatchingLayerProxy
	implements DispatchingLayerProxy, InputMessageListener {

    private static final Logger log = LoggerFactory
	    .getLogger(WRMDispatchingLayerProxy.class);

    public static final String CLIENT_OID = "WRMDispatchingLayerProxy-clientOID";

    private final OID clientOID;
    private final Transparency manager;
    private final NID hostNID;
    
    private boolean connected = false;

    /**
     * @param invoker
     */
    @Inject
    public WRMDispatchingLayerProxy(RemoteInvocationMessageHandler handler,
	    @Named(CLIENT_OID) OID clientOID, Transparency manager, @Named("node_id") NID hostNID) {
	super(handler);
	this.clientOID = clientOID;
	this.manager = manager;

	this.manager.register(RILMessage.class, this);

	this.manager.connect();
	
	this.hostNID = hostNID;

	// try {
	// Thread.sleep(3000);
	// } catch (InterruptedException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
    }
    
    private void connectToRM(){
	log.debug("Connecting to ReplicaManager's RIL");
	RegistryContentResponseMessage regResp = (RegistryContentResponseMessage) this.handleIncomingMessage(new RegistryContentRequestMessage());
	RILConnectionRequest req = new RILConnectionRequest(this.hostNID, regResp.getContent());
	Serializable resp = this.manager.send(req);
	if (!(resp instanceof RILConnectionResponse)) {
	    throw new TazioRuntimeException("Cannot connect to ReplicaManager's RIL, received response is :" + resp);
	} else {
	    this.connected = true;
	    log.debug("Connected to ReplicaManager's RIL");
	}
    }

//    private WRMDispatchingLayerProxy(RemoteInvocationMessageHandler handler,
//	    @Named(CLIENT_OID) OID clientOID, Transparency manager, long timeout) {
//	super(handler, timeout);
//	this.clientOID = clientOID;
//	this.manager = manager;
//
//	this.manager.register(RILMessage.class, this);
//
//	this.manager.connect();
//
//	try {
//	    Thread.sleep(3000);
//	} catch (InterruptedException e) {
//	    // TODO Auto-generated catch block
//	    e.printStackTrace();
//	}
//    }

    @Override
    protected void actuallySendMessage(RILRequestMessage msg)
	    throws FailedMessageDeliveryException {
	if (!connected) {
	    this.connectToRM();
	}
	
	try {

	    RILMessage response = (RILMessage) this.manager.send(msg);
	    
	    if (!(response instanceof RILResponseMessage)) {
		throw new TazioRuntimeException("Response received for " + msg
			+ " wasn't a RILResponseMessage but a "
			+ (response == null ? null : response.getClass()));
	    } else {
		this.responseReceived((RILResponseMessage) response);
	    }

	} catch (Exception e) {
	    log.debug("Error sending message", e);
	    throw new FailedMessageDeliveryException(msg);
	}
    }

    @Override
    protected void actuallySendMethodInvocationAtMostOnce(
	    MethodInvocationRequestMessage msg)
	    throws FailedMessageDeliveryException {
	if (!connected){
	    this.connectToRM();
	}
	
	try {

	    RILMessage response = (RILMessage) this.manager.sendAtMostOnce(msg);

	    if (!(response instanceof RILResponseMessage)) {
		throw new TazioRuntimeException(
			"Response received wasn't a RILResponseMessage");
	    } else {
		this.responseReceived((RILResponseMessage) response);
	    }

	} catch (Exception e) {
	    log.debug("Error sending message", e);
	    throw new FailedMessageDeliveryException(msg);
	}

    }

    @Override
    protected void actuallySendMethodInvocationToGroup(
	    MethodInvocationRequestMessage msg)
	    throws FailedMessageDeliveryException {
	
	if (!connected){
	    this.connectToRM();
	}

	// this method should never have been invoked!
	Preconditions.checkState(false);

	OnGroupMethodInvocationRequestMessage onGroupRequest = new OnGroupMethodInvocationRequestMessage(
		this.clientOID, msg);

	// this.expectingResponse(onGroupRequest);

	try {
	    RILMessage response = (RILMessage) this.manager
		    .sendAtMostOnce(onGroupRequest);

	    if (!(response instanceof RILResponseMessage)) {
		throw new TazioRuntimeException(
			"Response received wasn't a RILResponseMessage");
	    } else {
		this.responseReceived((RILResponseMessage) response);
	    }

	} catch (Exception e) {
	    log.debug("Error sending message", e);
	    throw new FailedMessageDeliveryException(msg);
	}
    }

    @Override
    public <T extends Serializable> Serializable handleMessage(NID sender,
	    T message) {
//	System.gc();
	
	Preconditions.checkArgument(message instanceof RILMessage);
	RILMessage msg = RILMessage.class.cast(message);

	RILMessage response = this.handleIncomingMessage(msg);

	return response;
    }

}
